﻿using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace ServerManager.Data
{
    /// <summary>
    /// Entity Framework repository
    /// </summary>
    public partial class EfRepository<T> : IRepository<T> where T : class
    {
        #region Fields

        private ApplicationDbContext _context;
        private DbSet<T> _entities;

        #endregion

        #region Ctor

        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="context">Object context</param>
        public EfRepository(ApplicationDbContext context)
        {
            this._context = context;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Get entity by identifier
        /// </summary>
        /// <param name="id">Identifier</param>
        /// <returns>Entity</returns>
        public virtual T GetById(object id)
        {
            return Entities.Find(id);
        }

        public T Find(Expression<Func<T, bool>> whereLambda)
        {
            T _entity = Entities.FirstOrDefault<T>(whereLambda);
            return _entity;
        }

        public IEnumerable<T> FindAll(Expression<Func<T, bool>> whereLambda)
        {
            if (whereLambda == null)
            {
                return Entities;
            }

            return Entities.Where(whereLambda);
        }


        public async Task<IEnumerable<T>> FindAllAsync(Expression<Func<T, bool>> whereLambda)
        {
            if (whereLambda == null)
            {
                return Entities;
            }

            return await Entities.Where(whereLambda).ToListAsync();
        }

        /// <summary>
        /// Insert entity
        /// </summary>
        /// <param name="entity">Entity</param>
        public virtual T Add(T entity)
        {
            if (entity == null)
                throw new ArgumentNullException("entity");

            this.Entities.Add(entity);

            this._context.SaveChanges();

            return entity;
        }

        /// <summary>
        /// Insert entity
        /// </summary>
        /// <param name="entity">Entity</param>
        public async virtual Task<T> AddAsync(T entity)
        {
            if (entity == null)
                throw new ArgumentNullException("entity");

            this.Entities.Add(entity);

            await _context.SaveChangesAsync();

            return entity;
        }

        /// <summary>
        /// Insert entities
        /// </summary>
        /// <param name="entities">Entities</param>
        public virtual void Add(IEnumerable<T> entities)
        {
            if (entities == null)
                throw new ArgumentNullException("entities");

            foreach (var entity in entities)
                this.Entities.Add(entity);

            this._context.SaveChanges();
        }

        /// <summary>
        /// Insert entities
        /// </summary>
        /// <param name="entities">Entities</param>
        public async virtual Task<IEnumerable<T>> AddAsync(IEnumerable<T> entities)
        {
            if (entities == null)
                throw new ArgumentNullException("entities");

            foreach (var entity in entities)
                this.Entities.Add(entity);

            await _context.SaveChangesAsync();
            return entities;
        }


        /// <summary>
        /// Update entity
        /// </summary>
        /// <param name="entity">Entity</param>
        public virtual int Update(T entity)
        {
            try
            {
                if (entity == null)
                    throw new ArgumentNullException("entity");

                _context.Entry(entity).State = EntityState.Modified;
                return this._context.SaveChanges();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return 0;
            }
            //try
            //{
            //    if (entity == null)
            //        throw new ArgumentNullException("entity");
            //    _context.Attach(entity);
            //    _context.Entry<T>(entity).State = EntityState.Modified;
            //    Entities.Update(entity);
            //    return _context.SaveChanges();
            //}
            //catch (Exception e)
            //{
            //    return 0;
            //}
        }

        public async virtual Task<int> UpdateAsync(T entity)
        {
            try
            {
                if (entity == null)
                    throw new ArgumentNullException("entity");

                _context.Entry(entity).State = EntityState.Modified;
                int i = await this._context.SaveChangesAsync();
                return i;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return 0;
            }
        }

        /// <summary>
        /// Update entities
        /// </summary>
        /// <param name="entities">Entities</param>
        public async virtual Task UpdateAsync(IEnumerable<T> entities)
        {
            try
            {
                if (entities == null)
                    throw new ArgumentNullException("entities");

                foreach (var entity in entities)
                    this.Entities.Update(entity);
                await this._context.SaveChangesAsync();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        //public virtual void Update(Expression<Func<T, bool>> filterExpression, Expression<Func<T, T>> updateExpression)
        //{
        //    try
        //    {
        //        Entities.Where(filterExpression).Update(updateExpression);
        //    }
        //    catch (Exception e)
        //    {

        //    }
        //}

        /// <summary>
        /// Delete entity
        /// </summary>
        /// <param name="entity">Entity</param>
        public virtual int Delete(Expression<Func<T, bool>> predicate)
        {
            try
            {
                var entitys = this.Entities.Where(predicate);
                foreach (var item in entitys)
                    this.Entities.Remove(item);

                return _context.SaveChanges();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return 0;
            }
        }

        public virtual async Task<int> DeleteAsync(Expression<Func<T, bool>> predicate)
        {
            try
            {
                var entitys = this.Entities.Where(predicate);
                foreach (var item in entitys)
                    this.Entities.Remove(item);

                return await _context.SaveChangesAsync();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return 0;
            }
        }

        /// <summary>
        /// Delete entities
        /// </summary>
        /// <param name="entities">Entities</param>
        public virtual void Delete(IEnumerable<T> entities)
        {
            try
            {
                if (entities == null)
                    throw new ArgumentNullException("entities");

                foreach (var entity in entities)
                    this.Entities.Remove(entity);

                this._context.SaveChanges();
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }

        public bool Exist(Expression<Func<T, bool>> anyLambda)
        {
            return Entities.Any(anyLambda);
        }

        public int Count(Expression<Func<T, bool>> predicate)
        {
            if (predicate == null)
                return Entities.Count();

            return Entities.Count(predicate);
        }

        //public virtual void Delete(Expression<Func<T, bool>> filterExpression)
        //{
        //    try
        //    {
        //        this.Entities.Where(filterExpression).Delete();
        //    }
        //    catch (Exception e)
        //    {

        //    }

        //}

        public ApplicationDbContext GetDbContext()
        {
            return _context;
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets a table
        /// </summary>
        public virtual IQueryable<T> Table
        {
            get
            {
                return this.Entities;
            }
        }

        /// <summary>
        /// Gets a table with "no tracking" enabled (EF feature) Use it only when you load record(s) only for read-only operations
        /// </summary>
        public virtual IQueryable<T> TableNoTracking
        {
            get
            {
                return this.Entities.AsNoTracking();
            }
        }

        /// <summary>
        /// Entities
        /// </summary>
        protected virtual DbSet<T> Entities
        {
            get
            {
                if (_entities == null)
                    _entities = _context.Set<T>();
                return _entities;
            }
        }

        #endregion
    }
}
